import { instantiate, assetManager, CCObject, JsonAsset, Prefab, director, Vec3, PhysicsSystem, Node, Asset, math, utils, RigidBody, geometry, systemEvent, SystemEventType, Camera, Touch, CameraComponent, PhysicsRayResult, Collider, Vec2 } from "cc"
import { Event } from "./event"
import { AssetLoadSync, AssetPath, IngameRequireAsset } from "../../utils/assethelp"
import { BoxConfig, InGameModel, ThingConfig, ThingShowoutConfig } from "./model/model";
import { EDITOR } from "cc/env";
import { Thing } from "./components/thing";

class ThingControler {

    private _curThings: Array<CCObject> = []
    private static _inc: ThingControler;

    public static get inc() {
        if (ThingControler._inc == null) {
            ThingControler._inc = new ThingControler()
        }
        return ThingControler._inc
    }

    clearThing() {
        for (let index = 0; index < this._curThings.length; index++) {
            const cobj = this._curThings[index];
            cobj.destroy()
        }


        this._curThings.splice(this._curThings.length)
    }

    private async crtThingbyConf(conf: ThingShowoutConfig): Promise<Node> {
        return new Promise(async (resolve, _) => {
            let prefab = await AssetLoadSync.loadRes<Prefab>(AssetPath.CONFIG.get(conf.Thing.Model) || "")
            let node = instantiate(prefab)
            let comp = node.addComponent(Thing)
            resolve(node)
        })
    }

    async genThingbyConfig(thingcfgs: ThingShowoutConfig[]): Promise<Node[]> {
        return new Promise(async (resolve, _) => {
            let nlist: Node[] = []
            for (let i = 0; i < thingcfgs.length; i++) {
                const conf = thingcfgs[i]
                const box = await this.crtThingbyConf(conf).then((ret) => {
                    nlist.push(ret)
                })
            }
            resolve(nlist)
        })
    }
}

class BoxControler {
    private _boxStore: Array<CCObject> = []
    private static _inc: BoxControler;


    public static get inc() {
        if (BoxControler._inc == null) {
            BoxControler._inc = new BoxControler()
        }
        return BoxControler._inc
    }

    private async crtBoxbyConf(conf: BoxConfig): Promise<Node> {
        return new Promise(async (resolve, _) => {
            let node = await AssetLoadSync.loadRes<Prefab>(AssetPath.CONFIG.get("prefab_box") || "")
            resolve(instantiate(node))
        })
    }

    genBoxsbyConfig(boxconfs: BoxConfig[]): Promise<Node[]> {
        return new Promise(async (resolve, rej) => {
            let nlist: Node[] = []
            for (let i = 0; i < boxconfs.length; i++) {
                const conf = boxconfs[i]
                const box = await this.crtBoxbyConf(conf).then((ret) => {
                    nlist.push(ret)
                })
            }
            resolve(nlist)
        })
    }
}

class InGameProxy extends ne.Proxy {
    private _debugNode!: Node;
    private _deskNode!: Node;
    private _popNode!: Node;

    private _mainCamera!: CameraComponent;
    private _dragThing!: Thing | undefined;
    private _ray!: geometry.Ray;
    private _offset!: Vec3;

    constructor(module: ne.Module) {
        super(module)
    }

    private initPhyWorld() {
        console.log("init phy world!")
        PhysicsSystem.instance.enable = true
        let body = utils.find("panel_desk")?.getComponent(RigidBody);
        body?.wakeUp()
        // PhysicsSystem.instance.gravity = new Vec3(0,  -9.8, 0)
    }

    private initVar() {
        this._deskNode = utils.find("node_boxstart")!
        this._popNode = utils.find("p_append")!
        this._debugNode = utils.find("debug")!
        this._mainCamera = utils.find("Main Camera")?.getComponent(Camera)!

        this._ray = new geometry.Ray()
    }

    private initRayTouch() {
        systemEvent.on(SystemEventType.TOUCH_START, this.chkTouchStart, this)
        systemEvent.on(SystemEventType.TOUCH_MOVE, this.chkTouchMove, this)
        systemEvent.on(SystemEventType.TOUCH_CANCEL, this.chkTouchCancel, this)
    }

    private converTouch2World(screenpos: Vec2): Vec3 {
        //convert y

        let camert_h = this._mainCamera.node.position.y
        // screen
        return new Vec3()
    }

    async getIngameConfig(): Promise<JsonAsset> {
        let ret = AssetLoadSync.loadRes<JsonAsset>(AssetPath.CONFIG.get("ingame_config") || "")
        return ret
    }

    registerStringEvents() {
        return [
            Event.InGameInit,
            Event.InGamePreload
        ]
    }

    receivedStringEvents(strevt: string, args: any) {
        let method_name = "on%s".format(strevt)
        let method = Reflect.get(this, method_name)
        const self = this
        if (typeof (method) == "function") {
            method.bind(self)(args)
        }
        else {
            console.warn("Default handler to execute method<%s> not found!", method_name)
        }
    }

    private debugTest() {
        if (!EDITOR) {
            this._debugNode.removeFromParent()
        }
    }

    private boxesFadeIn(boxes: Node[]) {
        for (const idx in boxes) {
            if (Object.prototype.hasOwnProperty.call(boxes, idx)) {
                const el = boxes[idx];
                this._deskNode.addChild(el)
                el.position = new Vec3(1.25 + parseInt(idx) * 2.5, 0, 1)
            }
        }
    }

    private thingsFadeIn(things: Node[]) {
        console.log(things, "-->>things")
        for (const idx in things) {
            if (Object.prototype.hasOwnProperty.call(things, idx)) {
                // if (Object.prototype.hasOwnProperty.call(things, idx) && idx == "0") {
                const el = things[idx];
                this._popNode.addChild(el)
                el.position = new Vec3(0, 5, 0)
                el.getComponent(Thing)?.addPhysicsProperty()
            }
        }
    }

    private chkTouchStart(touch: Touch) {
        let touch_x, touch_y
        touch_x = touch.getLocationX()
        touch_y = touch.getLocationY()
        console.log(touch_x, touch_y, "__ontouch start")
        this._mainCamera.screenPointToRay(touch_x, touch_y, this._ray)
        if (!this._mainCamera) return;
        let sys_phy = PhysicsSystem.instance
        if (sys_phy.raycast(this._ray)) {
            const r = sys_phy.raycastResults
            // console.log(r, "-->>r")
            if (r.length > 2) {
                console.log("on touch something...")
                this.onTouchThing(r, new Vec2(touch_x, touch_y))
            }
        }
    }

    private chkTouchMove(touch: Touch) {
        if (!this._dragThing) return;
        let touch_x, touch_y
        touch_x = touch.getLocationX()
        touch_y = touch.getLocationY()
        this._mainCamera.screenPointToRay(touch_x, touch_y, this._ray)
        let dir = new Vec3()    //物体在相机的相对方向
        // Vec3.subtract(dir, this._dragThing?.node.position! , new Vec3(touch.getLocationX(),touch.getLocationY(), 0.5))
        let out = this._mainCamera.screenToWorld(new Vec3(touch.getLocationX(), touch.getLocationY(), 0))
        let out2 = new Vec3()
        this._dragThing.node.setPosition(out.x, out.y, out.z)

        // let out2 = new Vec3()
        // Vec3.projectOnPlane(out2, dir, this._mainCamera.node.forward)
        // this._dragThing.holdMove(out2)
    }

    private chkTouchCancel(touch: Touch) {
        this._dragThing = undefined
    }

    private onTouchThing(ret: PhysicsRayResult[], startpos: Vec2) {
        let thing, phy_drag, desk_ret
        let isdrag, worldpos
        for (const idx in ret) {
            if (Object.prototype.hasOwnProperty.call(ret, idx)) {
                const el = ret[idx];
                console.log(el.collider.name, "-->colorname")
                switch (el.collider.TYPE) {
                    case Collider.Type.MESH:
                        thing = el.collider
                        break;
                    case Collider.Type.BOX:
                        if (el.collider.isTrigger == false){
                            desk_ret = el
                        }
                        else {
                            phy_drag = el
                        }
                        break
                    default:
                        break;
                }
                if (thing && desk_ret &&phy_drag) {
                    isdrag = true
                    worldpos = phy_drag?.hitPoint
                    break
                }
            }
        }

        if (isdrag == true) {
            console.log(worldpos, '-->>worldpos')
            if (worldpos != undefined){
                thing?.getComponent(Thing)?.holdOn(worldpos)
                this._dragThing = thing?.getComponent(Thing)!
            }
            
        }
    }

    // 游戏初始化
    private async onEVT_INGAME_INIT() {
        this.initVar()
        this.initPhyWorld()
        this.initRayTouch()
        this.debugTest()
        //config
        let gmodel = new InGameModel()
        let issuc = await gmodel.updateConfig()
        if (!issuc) {
            console.error("get the ingameconfig failed!")
            return
        }
        //清理桌面
        ThingControler.inc.clearThing()
        //创建盒子
        let lvconfig = gmodel.getConfigbyLvid(1)
        console.debug("-------------lvconfig-----------")
        console.log(lvconfig)
        console.debug("-------------lvconfig-----------")
        let boxes = await BoxControler.inc.genBoxsbyConfig(lvconfig.Boxes)
        this.boxesFadeIn(boxes)
        console.log("init finish1")
        //创建玩具
        let things = await ThingControler.inc.genThingbyConfig(lvconfig.Things)
        console.log("init finis2222")
        this.thingsFadeIn(things)
        console.log("init finish!")
    }

    // 游戏预加载
    private async onEVT_INGAME_PRELOAD() {
        console.log("preload start")
        console.log(assetManager.resources)
        //preload只会下载不会解析
        await assetManager.resources?.load(IngameRequireAsset, (curper, maxper, req) => {
            console.log("preload res..", curper, maxper, req)
        }, () => {
            console.log("preload suc!")
            ne.EventManager.dispatchStringEvent(Event.InGameInit)
        })
    }
}

export class InGameModule extends ne.Module {

    registerProxys() {
        return [
            InGameProxy
        ]
    }

    onStart() {
        ne.EventManager.dispatchStringEvent(Event.InGamePreload)

    }
}




